package ca.ericslandry.client.mvp.presenter;

import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.presenter.client.EventBus;
import ca.ericslandry.client.GwtCallback;
import ca.ericslandry.shared.domain.User;
import ca.ericslandry.shared.event.AuthenticationVerificationEvent;
import ca.ericslandry.shared.event.LoginEvent;
import ca.ericslandry.shared.event.LogoutEvent;
import ca.ericslandry.shared.event.UserUpdatedEvent;
import ca.ericslandry.shared.rpc.CheckSession;
import ca.ericslandry.shared.rpc.CheckSessionResult;
import ca.ericslandry.shared.rpc.GetUser;
import ca.ericslandry.shared.rpc.GetUserResult;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Window;
import com.google.inject.Inject;

public class HeaderPresenter extends BasePresenter<HeaderPresenter.Display> {
	public interface Display extends BaseDisplay {
		HasClickHandlers getLogin();

		HasClickHandlers getLogout();

		void showLogin();

		void showLogout();

		void showUser(User user);

		void showUserLoading();
	}

	private final DispatchAsync dispatcher;
	private static EventBus bus;
	@SuppressWarnings("unused")
	private final UserPresenter userPresenter;

	@Inject
	public HeaderPresenter(Display display, EventBus eventBus, DispatchAsync dispatcher, UserPresenter userPresenter) {
		super(display, eventBus);
		bus = eventBus;
		this.dispatcher = dispatcher;
		this.userPresenter = userPresenter;
		bind();
	}

	@Override
	protected void onBind() {
		Log.debug("onBind: header");
		// SETUP: register Javascript OpenID callback
		registerJSMethod();
		// ACTION: login
		registerHandler(display.getLogin().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doLogin();
			}
		}));
		// ACTION: logout
		registerHandler(display.getLogout().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doLogout();
			}
		}));
		// EVENT: user update
		registerHandler(eventBus.addHandler(UserUpdatedEvent.TYPE, new UserUpdatedEvent.Handler() {
			@Override
			public void onUserUpdated(UserUpdatedEvent event) {
				display.showUser(event.getUpdatedUser());
			}
		}));
		// EVENT: auth check
		registerHandler(eventBus.addHandler(AuthenticationVerificationEvent.TYPE,
				new AuthenticationVerificationEvent.Handler() {
					@Override
					public void onVerify(AuthenticationVerificationEvent event) {
						if (event.isVerified()) {
							showUser(event.getUserId());
						} else {
							// TODO: fix this
							Window.alert("Failed auth verification");
						}
					}
				}));
		// EVENT: successful login
		registerHandler(eventBus.addHandler(LoginEvent.TYPE, new LoginEvent.Handler() {
			@Override
			public void onLogin(LoginEvent event) {
				display.showUser(event.getUser());
				display.showLogout();
			}
		}));
		checkSession();
	}

	private void showUser(Long id) {
		dispatcher.execute(new GetUser(id), new GwtCallback<GetUserResult>(dispatcher, eventBus, display) {
			@Override
			public void callback(GetUserResult result) {
				display.showUser(result.getUser());
			}
		});
	}

	private void checkSession() {
		// is user already logged in?
		dispatcher.execute(new CheckSession(), new GwtCallback<CheckSessionResult>(dispatcher, eventBus, display) {
			@Override
			public void callback(CheckSessionResult result) {
				if (result.isValid()) {
					User user = result.getUser();
					eventBus.fireEvent(new LoginEvent(user));
				} else {
					// doLogout();
				}
			}
		});
	}

	protected void doLogout() {
		RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, "/logout/");
		try {
			builder.sendRequest(null, new RequestCallback() {
				public void onError(Request request, Throwable exception) {
					Window.alert("Error logging out:" + exception.getMessage());
				}

				public void onResponseReceived(Request request, Response response) {
					// do nothing
				}
			});
		} catch (RequestException e) {
			Window.alert("Unable to build the request.");
		}
		eventBus.fireEvent(new LogoutEvent());
		display.showLogin();
	}

	protected void doLogin() {
		int top = Window.getClientHeight() / 2 - 125;
		int left = Window.getClientWidth() / 2 - 250;
		display.showUserLoading();
		display.showLogout();
		String features = "status=0,toolbar=0,menubar=0,location=0,resizable=1,width=500,height=250,top=" + top
				+ ",left=" + left;
		Window.open("/home/?popup=true", "_blank", features);
	}

	/**
	 * Add a GWT javascript hook for OpenID callback
	 */
	private native void registerJSMethod() /*-{
		$wnd.handleOpenIDResponse = @ca.ericslandry.client.mvp.presenter.HeaderPresenter::handleOpenIDResponse(Ljava/lang/String;);
	}-*/;

	/**
	 * Parses the OpenID response to extract the identity
	 */
	public static void handleOpenIDResponse(String response) {
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/popup_verify/?" + response);
		try {
			builder.sendRequest(null, new RequestCallback() {
				public void onError(Request request, Throwable exception) {
					Window.alert("Error verifying:" + exception.getMessage());
				}

				public void onResponseReceived(Request request, Response response) {
					if (response.getStatusCode() == 401) {
						Log.warn("Authentication verification failed.");
						bus.fireEvent(new AuthenticationVerificationEvent(false, null));
					} else {
						Log.info("Authentication verification completed successfully.");
						bus.fireEvent(new AuthenticationVerificationEvent(true, new Long(response.getText())));
					}
				}
			});
		} catch (RequestException e) {
			Window.alert("Unable to build the request.");
		}
	}
}
